/**
 * 
 */
package za.co.sindi.oauth.core.service.director;

import org.apache.log4j.Logger;

import za.co.sindi.oauth.core.context.ApplicationContext;
import za.co.sindi.oauth.core.context.RequestContext;
import za.co.sindi.oauth.core.context.ResponseContext;
import za.co.sindi.oauth.core.result.Result;
import za.co.sindi.oauth.core.service.AnnotatedService;
import za.co.sindi.oauth.core.service.DefaultServiceCatalog;
import za.co.sindi.oauth.core.service.Service;
import za.co.sindi.oauth.core.service.ServiceCatalog;
import za.co.sindi.oauth.core.service.ServiceDirector;
import za.co.sindi.oauth.exception.ResultException;
import za.co.sindi.oauth.exception.ServiceDirectorException;
import za.co.sindi.oauth.exception.ServiceException;
import za.co.sindi.oauth.server.config.ModuleConfig;
import za.co.sindi.oauth.server.config.RequestConfig;
import za.co.sindi.oauth.server.config.ServiceConfig;
import za.co.sindi.oauth.util.ApplicationUtil;

/**
 * @author Buhake Sindi
 * @since 11 April 2012
 *
 */
public abstract class AbstractServiceDirector implements ServiceDirector {

	protected final Logger logger = Logger.getLogger(this.getClass());
	private ModuleConfig moduleConfig;
	
	/* (non-Javadoc)
	 * @see com.neurologic.oauth.core.service.ServiceDirector#init(com.neurologic.oauth.server.config.ModuleConfig)
	 */
	@Override
	public void init(ModuleConfig moduleConfig) {
		// TODO Auto-generated method stub
		this.moduleConfig = moduleConfig;
	}
	
	/* (non-Javadoc)
	 * @see com.neurologic.oauth.core.service.ServiceDirector#execute(com.neurologic.oauth.core.context.ApplicationContext)
	 */
	@Override
	public void execute(ApplicationContext context) throws ServiceDirectorException {
		// TODO Auto-generated method stub
		if (context == null) {
			throw new IllegalArgumentException("An application context (" + ApplicationContext.class.getName() + ") is required.");
		}
		
		try {
			RequestContext request = context.getRequestContext();
			ResponseContext response = context.getResponseContext();
			
			String pathInfo = getPathInfo(request);
			ServiceConfig serviceConfig = moduleConfig.getServiceConfigByPath(pathInfo);
			if (serviceConfig == null) {
				throw new ServiceDirectorException("No service found for context path '" + pathInfo + "'.");
			}
			
			String className = serviceConfig.getClassName();
			ServiceCatalog catalog = DefaultServiceCatalog.getInstance();
			Service service = catalog.getService(serviceConfig.getClassName());
			if (service == null) {
				//It doesn't exist on catalog, create one.
				if (serviceConfig.isServiceSubclass()) {
					service = ApplicationUtil.applicationInstance(className);
				} else if (serviceConfig.isAnnotated()) {
					service = new AnnotatedService(className);
				}
				
				//Now, we shouldn't get a null service.
				if (service == null) {
					throw new ServiceDirectorException("Cannot create service for class '" + className + "'.");
				}
				
				//Save
				catalog.addService(className, service);
			}
			
			//Get RequestConfig
			RequestConfig requestConfig = null;
			RequestConfig[] requestConfigs = serviceConfig.getRequestConfigs();
			if (requestConfigs != null) {
				String method = getRequestMethod(context.getRequestContext());
				for (RequestConfig config : requestConfigs) {
					if (config.getMethod().equals(method)) {
						requestConfig = config;
					}
				}
			}
			
			
			//Now, service
			Result result = service.service(requestConfig, request, response);
			if (result == null) {
				if (logger.isInfoEnabled()) {
					logger.info("Service returned a null result, cannot execute further.");
				}
			} else {
				if (logger.isInfoEnabled()) {
					logger.info("Executing result of type " + result.getClass().getName());
				}
				
				result.executeResult(context);
			}
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			throw new ServiceDirectorException("execute()", e);
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			throw new ServiceDirectorException("execute()", e);
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			throw new ServiceDirectorException("execute()", e);
		} catch (ServiceException e) {
			// TODO Auto-generated catch block
			throw new ServiceDirectorException("execute()", e);
		} catch (ResultException e) {
			// TODO Auto-generated catch block
			throw new ServiceDirectorException("execute()", e);
		}
	}
	
	protected abstract String getPathInfo(RequestContext request);
	protected abstract String getRequestMethod(RequestContext request);
}
